package com.zhs.type;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;

public class Type {
	private static Hashtable<String, Type> allTypes = new Hashtable<String, Type>();
	private String typeName;

	// if this type is a primary type, then the following fields
	// may be left null.

	// methods does not allow overloading
	private Hashtable<String, Method> methods = new Hashtable<String, Method>();
	private Scope scope = new Scope();
	private String superTypeName;

	public String getTypeName() {
		return typeName;
	}

	public Collection<Method> getExclusiveMethods() {
		return this.methods.values();
	}

	public Collection<Method> getMethods() {
		if (superTypeName != null) {
			Type superType = allTypes.get(superTypeName);
			Collection<Method> collection = superType.getMethods();
			for (Method m : methods.values()) {
				collection.add(m);
			}
			return collection;
		} else {
			ArrayList<Method> list = new ArrayList<Method>(methods.values());
			return list;
		}
	}

	public Scope getScope() {
		if (superTypeName != null) {
			Type superType = allTypes.get(superTypeName);
			// do not change superType's scope
			Scope s = superType.getScope().clone();
			s.addScope(this.scope);
			return s;
		} else {
			return scope;
		}
	}

	public String getSuperType() {
		return superTypeName;
	}

	public void setTypeName(String id) {
		this.typeName = id;
	}

	public void setScope(Scope scope) {
		this.scope = scope;
	}

	public void setSuperType(String superType) {
		this.superTypeName = superType;
	}

	public boolean isPrimaryType() {
		return this.typeName.equals("int") || this.typeName.equals("double")
				|| this.typeName.equals("boolean");
	}

	public boolean isArrayType() {
		return this.typeName.equals("intArray")
				|| this.typeName.equals("doubleArray");
	}

	public Method addMethod(Method method) {
		Method m = lookupMethod(method.getMethodName());
		if (m != null) {
			return m;
		}// already a method exists... either in this type or in a super type.
		// no overloading is allowed.

		return this.methods.put(method.getMethodName(), method);
	}

	public Method lookupMethod(String methodName) {
		Method m = methods.get(methodName);
		if (m == null && superTypeName != null) {
			m = Type.lookupType(superTypeName).lookupMethod(methodName);
		}
		return m;
	}

	/**
	 * Finds the type with given name
	 * 
	 * @param typeName
	 * @return corresponding Type reference, null if not found
	 */
	public static Type lookupType(String typeName) {
		return allTypes.get(typeName);
	}

	/**
	 * Adds type reference to the allTypes list.
	 * 
	 * @param type
	 * @return true if ok
	 */
	public static boolean registerType(Type type) {
		if (allTypes.containsKey(type.getTypeName())) {
			return false;
		}
		allTypes.put(type.getTypeName(), type);
		return true;
	}

	public static Collection<Type> getAllTypes() {
		return allTypes.values();
	}

	public static void reset() {
		allTypes = null;
		allTypes = new Hashtable<String, Type>();
	}
}
